//
// Created by wxd on 2025/7/6.
//

#include "Application.h"

#include <iostream>

#include "RenderEngineLogger.h"
#include "../../wrapper/log/Logger.h"
#include "../../wrapper/monitor/GLDebug.hpp"

#include "../imgui/imgui.h"
#include "../imgui/imgui_impl_glfw.h"
#include "../imgui/imgui_impl_opengl3.h"
#include "glm/glm.hpp"


#define APPLICATION_PACKAGE_NAME "application/app/"

// 初始化静态成员
std::unique_ptr<Application> Application::m_Instance = nullptr;
std::once_flag Application::m_Initialized;
WindowEvent Application::m_WindowEvent = WindowEvent::NONE;

Application& Application::getInstance() {
    std::call_once(m_Initialized, &Application::create);
    return *m_Instance;
}

Application::Application() {
#ifdef LOG_DEBUG
    LOG_DEBUG_METHOD(APPLICATION_PACKAGE_NAME, "Application()");
#else
#endif
    m_GLWindow = nullptr;
    m_WindowConfig = nullptr;
    m_WindowStatus = GL_StatusType::NONE;
    m_WindowEvent = WindowEvent::NONE;
    m_GLWindowColor = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
}

Application::~Application() {
#ifdef LOG_DEBUG
    LOG_DEBUG_METHOD(APPLICATION_PACKAGE_NAME, "~Application()");
#else
#endif
    if (m_GLWindow != nullptr) {
        glfwDestroyWindow(m_GLWindow);
    }
}


/***********************************************GLFW事件指针的具体实现*****************************************************/
void Application::framebufferSizeCallback(GLFWwindow *window, int width, int height) {
    if (auto self = static_cast<Application*>(glfwGetWindowUserPointer(window)); self->m_ResizeCallback.has_value()) {
        m_WindowEvent = WindowEvent::RESIZE;
        (*self->m_ResizeCallback)(width, height);
        LOG_DEBUG_METHOD("", "framebufferSizeCallback");
    }
}

void Application::keyBoardCallback(GLFWwindow *window, int key, int scancode, int action, int mods) {
    if (auto self = static_cast<Application*>(glfwGetWindowUserPointer(window)); self->m_KeyBoardCallback.has_value()) {
        m_WindowEvent = WindowEvent::KEYBOARD;
        (*self->m_KeyBoardCallback)(key, action, mods);
        LOG_DEBUG_METHOD("", "keyBoardCallback");
    }
}

void Application::mouseCallback(GLFWwindow *window, int button, int action, int mods) {
    if (auto self = static_cast<Application*>(glfwGetWindowUserPointer(window)); self->m_MouseCallback.has_value()) {
        m_WindowEvent = WindowEvent::MOUSE_DOWN;
        (*self->m_MouseCallback)(button, action, mods);
        LOG_DEBUG_METHOD("", "mouseCallback");
    }
}

void Application::cursorCallback(GLFWwindow *window, double xPos, double yPos) {
    if (auto self = static_cast<Application*>(glfwGetWindowUserPointer(window)); self->m_CursorCallback.has_value()) {
        m_WindowEvent = WindowEvent::MOUSE_MOVE;
        (*self->m_CursorCallback)(xPos, yPos);
        LOG_DEBUG_METHOD("", "cursorCallback");
    }
}

void Application::mouseScrollCallback(GLFWwindow *window, double xOffset, double yOffset) {
    if (auto self = static_cast<Application*>(glfwGetWindowUserPointer(window)); self->m_MouseScrollCallback.has_value()) {
        m_WindowEvent = WindowEvent::MOUSE_SCROLL;
        (*self->m_MouseScrollCallback)(xOffset, yOffset);
        LOG_DEBUG_METHOD("", "mouseScrollCallback");
    }
}


/***********************************************GLFW事件回调的注册方法*****************************************************/
void Application::setResizeCallback(const onResizeCallback& callback) {
    m_ResizeCallback = callback;
    LOG_DEBUG_METHOD("", "setResizeCallback");
}

void Application::setKeyBoardCallback(const onKeyBoardCallback &callback) {
    m_KeyBoardCallback = callback;
    LOG_DEBUG_METHOD("", "setKeyBoardCallback");
}

void Application::setMouseCallback(const onMouseCallback &callback) {
    m_MouseCallback = callback;
    LOG_DEBUG_METHOD("", "setMouseCallback");
}

void Application::setCursorCallback(const onCursorCallback &callback) {
    m_CursorCallback = callback;
    LOG_DEBUG_METHOD("", "setCursorCallback");
}

void Application::setMouseScrollCallback(const onMouseScrollCallback &callback) {
    m_MouseScrollCallback = callback;
    LOG_DEBUG_METHOD("", "setMouseScrollCallback");
}

void Application::setHideMouseCursor(bool isHide) {
    m_IsHideMouseCursor = isHide;
}


GLFWwindow *Application::getCurrentWindow() const {
    return m_GLWindow;
}

WindowConfig *Application::getWindowConfig() const {
    return m_WindowConfig.get();
}

uint32_t Application::getCurrentWindowWidth() const {
    if (m_WindowConfig != nullptr) {
        return m_WindowConfig->m_Width;
    }
    return 0;
}

uint32_t Application::getCurrentWindowHeight() const {
    if (m_WindowConfig != nullptr) {
        return m_WindowConfig->m_Height;
    }
    return 0;
}

GL_StatusType Application::getCurrentWindowStatus() const {
    return m_WindowStatus;
}

glm::vec4 Application::getCurrentWindowColor() const {
    return m_GLWindowColor;
}

float Application::getWindowAspectRatio() const {
    const auto h = static_cast<float>(m_WindowConfig->m_Height);
    if (m_WindowConfig != nullptr && h > 0.f) {
        const auto w = static_cast<float>(m_WindowConfig->m_Width);
        return w / h;
    }
    return 0.f;
}

void Application::getCurrentCursorPos(double *xpos, double *ypos) const {
    glfwGetCursorPos(m_GLWindow, xpos, ypos);
}

std::pair<int, int> Application::getDeviceScreenSize() {
    const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
    int screenWidth = 0;
    int screenHeight = 0;
    if (mode) {
        screenWidth = mode->width;
        screenHeight = mode->height;
    }
    return { screenWidth, screenHeight };
}


void Application::initWindowConfig(int major, int minor, int profile,
                                   uint32_t width, uint32_t height, const char *title) {
    m_WindowConfig = std::make_unique<WindowConfig>();
    m_WindowConfig->m_majorVersion = major;
    m_WindowConfig->m_minorVersion = minor;
    m_WindowConfig->m_Profile = profile;
    m_WindowConfig->m_Width = width;
    m_WindowConfig->m_Height = height;
    m_WindowConfig->m_Title = title;

    m_WindowConfig->toString();
}

void Application::initImGUI() const {
    if (!m_GLWindow) {
        return;
    }
    ImGui::CreateContext();
    ImGui::StyleColorsDark();
    ImGui_ImplGlfw_InitForOpenGL(m_GLWindow, true);
    ImGui_ImplOpenGL3_Init("#version 330");
}

void Application::openImGUIRenderFrame() const {
    if (!m_GLWindow) {
        return;
    }
    ImGui_ImplOpenGL3_NewFrame();
    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();
}

void Application::renderImGUIFrame() const {
    if (!m_GLWindow) {
        return;
    }
    ImGui::Render();
    int display_w, display_h;
    glfwGetFramebufferSize(m_GLWindow, &display_w, &display_h);
    glViewport(0, 0, display_w, display_h);
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}

bool Application::init() {
#ifdef LOG_DEBUG
    LOG_DEBUG_METHOD(APPLICATION_PACKAGE_NAME, "init()");
#else
#endif

    m_WindowStatus = GL_StatusType::INIT;

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, m_WindowConfig->m_majorVersion);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, m_WindowConfig->m_minorVersion);
    glfwWindowHint(GLFW_OPENGL_PROFILE, m_WindowConfig->m_Profile);

    m_GLWindow = glfwCreateWindow(
        static_cast<int>(m_WindowConfig->m_Width),
        static_cast<int>(m_WindowConfig->m_Height),
        m_WindowConfig->m_Title,
        nullptr, nullptr
    );

    if (m_GLWindow == nullptr) {
        LOG_DEBUG_METHOD("", "Failed to create GLFW window");
        glfwTerminate();
        return false;
    }

    glfwMakeContextCurrent(m_GLWindow);

    if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))) {
        LOG_DEBUG_METHOD("", "Failed to initialize GLAD");
        return false;
    }

    // this就是当前全局唯一的Application对象
    glfwSetWindowUserPointer(m_GLWindow, this);

    if (mEnableShowImGUI) {
        initImGUI();
    }

    mGLInited = true;

    // 注册GLFW事件
    if (m_ResizeCallback.has_value()) {
        glfwSetFramebufferSizeCallback(m_GLWindow, &Application::framebufferSizeCallback);
        LOG_DEBUG_METHOD("", "[init]: glfwSetFramebufferSizeCallback");
    }

    if (m_KeyBoardCallback.has_value()) {
        glfwSetKeyCallback(m_GLWindow, &Application::keyBoardCallback);
        LOG_DEBUG_METHOD("", "[init]: glfwSetKeyCallback");
    }

    if (m_MouseCallback.has_value()) {
        glfwSetMouseButtonCallback(m_GLWindow, &Application::mouseCallback);
        LOG_DEBUG_METHOD("", "[init]: glfwSetMouseButtonCallback");
    }

    if (m_CursorCallback.has_value()) {
        glfwSetCursorPosCallback(m_GLWindow, &Application::cursorCallback);
        LOG_DEBUG_METHOD("", "[init]: glfwSetCursorPosCallback");
    }

    if (m_MouseScrollCallback.has_value()) {
        glfwSetScrollCallback(m_GLWindow, &Application::mouseScrollCallback);
        LOG_DEBUG_METHOD("", "[init]: glfwSetScrollCallback");
    }

    if (m_IsHideMouseCursor) {
        glfwSetInputMode(m_GLWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    }
    return true;
}

bool Application::update() {
#ifdef LOG_DEBUG
   // LOG_DEBUG_METHOD(APPLICATION_PACKAGE_NAME, "update()");
#else
#endif

    if (glfwWindowShouldClose(m_GLWindow)) {
        return false;
    }

    if (mEnableShowImGUI) {

    }

    m_WindowStatus = GL_StatusType::RENDERING;
    glClearColor(m_GLWindowColor.r, m_GLWindowColor.g, m_GLWindowColor.b, m_GLWindowColor.a);
    glfwPollEvents();
    glfwSwapBuffers(m_GLWindow);

    return true;
}

void Application::destroy() {
#ifdef LOG_DEBUG
    LOG_DEBUG_METHOD(APPLICATION_PACKAGE_NAME, "destroy()");
#else
#endif

    m_WindowStatus = GL_StatusType::RENDERING;

    glfwTerminate();
}

bool Application::isGLInited() const {
    return mGLInited;
}